/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * SocketChatSD.java
 *
 * Created on 16/10/2009, 23:56:16
 */
package br.edu.fa7.socketchatsd.principal;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.CharBuffer;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jdesktop.swingworker.SwingWorker;
import org.omg.PortableServer.THREAD_POLICY_ID;

/**
 *
 * @author alysonmelo
 */
public class SocketChatSD extends javax.swing.JFrame {

    private Socket conexao;
    private static boolean done = false;
    //Variáveis de log
    private String log, chat;
    //Variável criada para garantir que a aba só será mudada uma vez
    private Boolean mudarAba = false;
    // Parte que controla as conexões por meio de  threads.
    // Note que a instanciação está no main.
    private static Vector clientes;
    // nome deste cliente
    private String meuNome;
    // construtor que recebe o socket deste cliente
    private Boolean clienteSocket = false;

    /** Creates new form SocketChatSD */
    public SocketChatSD() {
        log = "Aguardando...";
        chat = "";
        initComponents();
    }
    SwingWorker worker = new SwingWorker<String, Void>() {

        int i = 0;
        //This method automatically gets executed in a background thread

        public String doInBackground() {
            // System.out.println("worker");
            try {
                while (true) {

                    if (jCheckServer.isSelected()) {
                    }

                    if (!log.equals(jTextLog.getText())) {
                        jTextLog.setText(log);
                    }

                    if (!chat.equals(jTextChatScreen.getText())) {
                        jTextChatScreen.setText(chat);
                    }

                    //Verifica se a conexão está ativa
                    if (clienteSocket) {

                        //Muda de aba
                        if (!mudarAba) {
                            jTabbedPane1.setSelectedIndex(0);
                            mudarAba = true;
                        }
                        jButConectar.setText("Desconectar");


                    } else {
                        //Muda de aba
                        mudarAba = false;
                        jTabbedPane1.setSelectedIndex(1);
                        jButConectar.setText("Conectar");
                    }

                    Thread.sleep(2000);
                }

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return null;

        }

        //This methods automatically gets executed in the EDT
        public void done() {
        }
    };

    /**
     * Método para atualizar os componentes a cada 100 Ms
     */
    public void startClient() {

        new Thread() {

            public void run() {
                try {
                    BufferedReader entrada = new BufferedReader(new InputStreamReader(conexao.getInputStream()));
                    String linha;
                    while (true) {
                        // pega o que o servidor enviou
                        entrada.read(jTextWords.getText().toCharArray());
                        linha = jTextWords.getText();
                        // verifica se é uma linha válida. Pode ser que a conexão
                        // foi interrompida. Neste caso, a linha é null. Se isso
                        // ocorrer, termina-se a execução saindo com break
                        if (linha == null) {
                            System.out.println("Conexão encerrada!");
                            break;
                        }
                        // caso a linha não seja nula, deve-se imprimi-la
                        System.out.println();
                        System.out.println(linha);
                        System.out.print("...> ");
                    }
                } catch (IOException e) {
                    // caso ocorra alguma exceção de E/S, mostre qual foi.
                    System.out.println("IOException: " + e);
                }
                // sinaliza para o main que a conexão encerrou.
                done = true;
            }
        }.start();

    }

    public void serverClient() {

        new Thread() {

            public void run() {
                try {
                    // objetos que permitem controlar fluxo de comunicação
                    BufferedReader entrada = new BufferedReader(new InputStreamReader(conexao.getInputStream()));
                    PrintStream saida = new PrintStream(conexao.getOutputStream());
                    // primeiramente, espera-se pelo nome do cliente
                    entrada.read(jTextNickName.getText().toCharArray());
                    // agora, verifica se string recebida é valida, pois
                    // se a conexão foi interrompida, a string é null.
                    // Se isso ocorrer, deve-se terminar a execução.
                    if (meuNome == null) {
                        return;
                    }
                    // Uma vez que se tem um cliente conectado e conhecido,
                    // coloca-se fluxo de saída para esse cliente no vetor de
                    // clientes conectados.
                    clientes.add(saida);
                    // clientes é objeto compartilhado por várias threads!
                    // De acordo com o manual da API, os métodos são
                    // sincronizados. Portanto, não há problemas de acessos
                    // simultâneos.

                    // Loop principal: esperando por alguma string do cliente.
                    // Quando recebe, envia a todos os conectados até que o
                    // cliente envie linha em branco.
                    // Verificar se linha é null (conexão interrompida)
                    // Se não for nula, pode-se compará-la com métodos string
                    entrada.read(jTextWords.getText().toCharArray());
                    while (jTextWords.getText() != null && !(jTextWords.getText().trim().equals(""))) {
                        // reenvia a linha para todos os clientes conectados
                        sendToAll(saida, " disse: ", jTextWords.getText());
                        // espera por uma nova linha.
                        entrada.read(jTextWords.getText().toCharArray());
                    }
                    // Uma vez que o cliente enviou linha em branco, retira-se
                    // fluxo de saída do vetor de clientes e fecha-se conexão.
                    sendToAll(saida, " saiu ", "do chat!");
                    clientes.remove(saida);
                    conexao.close();
                } catch (IOException e) {
                    // Caso ocorra alguma excessão de E/S, mostre qual foi.
                    System.out.println("IOException: " + e);
                }
            }
        }.start();

    }

    // enviar uma mensagem para todos, menos para o próprio
    public void sendToAll(PrintStream saida, String acao,
            String linha) throws IOException {
        Enumeration e = clientes.elements();
        while (e.hasMoreElements()) {
            // obtém o fluxo de saída de um dos clientes
            PrintStream chat = (PrintStream) e.nextElement();
            // envia para todos, menos para o próprio usuário
            if (chat != saida) {
                chat.println(meuNome + acao + linha);
            }
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTabbedPane1 = new javax.swing.JTabbedPane();
        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTextChatScreen = new javax.swing.JTextArea();
        jScrollPane2 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jTextWords = new javax.swing.JTextField();
        jButEnviar = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();
        jTextIp = new javax.swing.JTextField();
        jTextPorta = new javax.swing.JTextField();
        jLabel3 = new javax.swing.JLabel();
        jButConectar = new javax.swing.JButton();
        jTextNickName = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        jScrollPane3 = new javax.swing.JScrollPane();
        jTextLog = new javax.swing.JTextArea();
        jLabel5 = new javax.swing.JLabel();
        jCheckServer = new javax.swing.JCheckBox();
        jButton1 = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setResizable(false);

        jPanel1.setOpaque(false);

        jTextChatScreen.setColumns(20);
        jTextChatScreen.setEditable(false);
        jTextChatScreen.setRows(5);
        jTextChatScreen.setWrapStyleWord(true);
        jScrollPane1.setViewportView(jTextChatScreen);

        jList1.setBorder(javax.swing.BorderFactory.createTitledBorder("Usuários"));
        jList1.setModel(new javax.swing.AbstractListModel() {
            String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
            public int getSize() { return strings.length; }
            public Object getElementAt(int i) { return strings[i]; }
        });
        jScrollPane2.setViewportView(jList1);

        jButEnviar.setText("Enviar");
        jButEnviar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButEnviarActionPerformed(evt);
            }
        });

        org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(jTextWords, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 521, Short.MAX_VALUE)
                    .add(jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 521, Short.MAX_VALUE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE)
                    .add(jButEnviar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 179, Short.MAX_VALUE))
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel1Layout.createSequentialGroup()
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jScrollPane2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 311, Short.MAX_VALUE)
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jScrollPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 311, Short.MAX_VALUE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jTextWords, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jButEnviar))
                .add(36, 36, 36))
        );

        jTabbedPane1.addTab("SocketChatSD", jPanel1);

        jPanel2.setOpaque(false);

        jLabel2.setText("Ip do Servidor");

        jTextIp.setText("127.0.0.1");

        jTextPorta.setText("4321");

        jLabel3.setText("Porta");

        jButConectar.setText("Conectar");
        jButConectar.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButConectarActionPerformed(evt);
            }
        });

        jTextNickName.setText("Anônimo");

        jLabel4.setText("Pseudônimo");

        jTextLog.setBackground(new java.awt.Color(204, 204, 204));
        jTextLog.setColumns(20);
        jTextLog.setEditable(false);
        jTextLog.setRows(5);
        jTextLog.setText("Aguardando...");
        jScrollPane3.setViewportView(jTextLog);

        jLabel5.setText("Registro de conexão");

        jCheckServer.setText("Servidor");
        jCheckServer.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jCheckServerActionPerformed(evt);
            }
        });

        org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                    .add(jPanel2Layout.createSequentialGroup()
                        .add(jScrollPane3, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 710, Short.MAX_VALUE)
                        .addContainerGap())
                    .add(jPanel2Layout.createSequentialGroup()
                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(jLabel2)
                            .add(jTextIp, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 497, Short.MAX_VALUE)
                            .add(jLabel4)
                            .add(jLabel5)
                            .add(jTextNickName, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 497, Short.MAX_VALUE))
                        .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                        .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
                            .add(jLabel3)
                            .add(org.jdesktop.layout.GroupLayout.TRAILING, jTextPorta, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 203, Short.MAX_VALUE)
                            .add(jCheckServer))
                        .addContainerGap())
                    .add(org.jdesktop.layout.GroupLayout.TRAILING, jPanel2Layout.createSequentialGroup()
                        .add(jButConectar)
                        .addContainerGap())))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jLabel3)
                    .add(jLabel2))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jTextPorta, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jTextIp, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel4)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(jTextNickName, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 21, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                    .add(jCheckServer))
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jLabel5)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(jScrollPane3, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 180, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
                .add(jButConectar)
                .add(9, 9, 9))
        );

        jTabbedPane1.addTab("Configuações", jPanel2);

        jTabbedPane1.setSelectedIndex(1);

        jButton1.setText("Sair");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
                    .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 771, Short.MAX_VALUE)
                    .add(jButton1))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .add(jTabbedPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 430, Short.MAX_VALUE)
                .add(18, 18, 18)
                .add(jButton1)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jCheckServerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckServerActionPerformed
        if (jCheckServer.isSelected()) {
            jTextIp.setEnabled(false);
        } else {
            jTextIp.setEnabled(true);
        }
    }//GEN-LAST:event_jCheckServerActionPerformed

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        System.exit(0);
    }//GEN-LAST:event_jButton1ActionPerformed

    private void jButEnviarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButEnviarActionPerformed
        PrintStream saida = null;
        try {
            saida = new PrintStream(conexao.getOutputStream());
        } catch (IOException ex) {
            Logger.getLogger(SocketChatSD.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (jTextWords.getText().length() > 0) {
            String frase = jTextNickName.getText() + ": " + jTextWords.getText() + "\n";

            jTextWords.setText("");
        }
    }//GEN-LAST:event_jButEnviarActionPerformed

    private void jButConectarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButConectarActionPerformed

        worker.execute();

        if (jCheckServer.isSelected()) {
            // instancia o vetor de clientes conectados
            clientes = new Vector();
            try {
                // criando um socket que fica escutando a porta 4321.
                ServerSocket s = new ServerSocket(Integer.parseInt(jTextPorta.getText()));
                // Loop principal.
                while (true) {
                    // aguarda algum cliente se conectar. A execução do
                    // servidor fica bloqueada na chamada do método accept da
                    // classe ServerSocket. Quando algum cliente se conectar
                    // ao servidor, o método desbloqueia e retorna com um
                    // objeto da classe Socket, que é porta da comunicação.
                    log = "Esperando alguem se conectar...";
                    Socket conexao = s.accept();
                    log += " Conectou!";
                    // cria uma nova thread para tratar essa conexão
                    this.conexao = conexao;
                    serverClient();
                    clienteSocket = true;
                    // voltando ao loop, esperando mais alguém se conectar.
                    }
            } catch (IOException e) {
                // caso ocorra alguma excessão de E/S, mostre qual foi.
                System.out.println("IOException: " + e);
            }
        } else {

            //Se as opções estivem preenchidas
            if (jTextIp.getText().length() > 0 && jTextPorta.getText().length() > 0) {

                try {

                    Socket conexao = new Socket(jTextIp.getText(), Integer.parseInt(jTextPorta.getText()));

                    // uma vez estabelecida a comunicação, deve-se obter os
                    // objetos que permitem controlar o fluxo de comunicação
                    PrintStream saida = new PrintStream(conexao.getOutputStream());
                    // enviar antes de tudo o nome do usuário
                    BufferedReader teclado =
                            new BufferedReader(new InputStreamReader(System.in));
                    log = "Entre com o seu nome: ";
                    char[] caracter = jTextNickName.getText().toCharArray();

                    teclado.read(caracter);
                    log = jTextNickName.getText();

                    // Uma vez que tudo está pronto, antes de iniciar o loop
                    // principal, executar a thread de recepção de mensagens.
                    this.conexao = conexao;

                    // loop principal: obtendo uma linha digitada no teclado e
                    // enviando-a para o servidor.
                    clienteSocket = true;
                    while (true) {
                        // ler a linha digitada no teclado
                        caracter = jTextChatScreen.getText().toCharArray();
                        teclado.read(caracter);
                        // antes de enviar, verifica se a conexão não foi fechada
                        if (done) {
                            break;
                        }
                        // envia para o servidor
                    }
                } catch (IOException e) {
                    // Caso ocorra alguma excessão de E/S, mostre qual foi.
                    log = "IOException: " + e;
                }
            }
        }

    }//GEN-LAST:event_jButConectarActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {

        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new SocketChatSD().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButConectar;
    private javax.swing.JButton jButEnviar;
    private javax.swing.JButton jButton1;
    private javax.swing.JCheckBox jCheckServer;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JList jList1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JTextArea jTextChatScreen;
    private javax.swing.JTextField jTextIp;
    private javax.swing.JTextArea jTextLog;
    private javax.swing.JTextField jTextNickName;
    private javax.swing.JTextField jTextPorta;
    private javax.swing.JTextField jTextWords;
    // End of variables declaration//GEN-END:variables
}
